package cn.com.zcode.auth.control;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.com.zcode.auth.model.UserToken;
import cn.com.zcode.core.util.HttpReqUtil;
import cn.com.zcode.user.model.User;
import cn.com.zcode.user.util.UserUtil;

public class AuthenticationFilter extends FormAuthenticationFilter {
	private static final Logger logger = LoggerFactory.getLogger(AuthenticationFilter.class);

	public static final String DEFAULT_CAPTCHA_PARAM = "captcha";

	public AuthenticationFilter() {
	}

	/**
	 * 登录验证
	 */
	@Override
	protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
		HttpServletRequest httpservletrequest = ((HttpServletRequest) request);
		String username = getUsername(request);
		String password = getPassword(request);
		String captcha = WebUtils.getCleanParam(request, DEFAULT_CAPTCHA_PARAM);
		boolean rememberMe = isRememberMe(request);
		String host = getHost(request);

		UserToken token = new UserToken(username, password.toCharArray(), rememberMe, host, captcha);

		boolean isFromMobile = HttpReqUtil.isFromMobile(httpservletrequest);
		String ip = UserUtil.getUserIp(httpservletrequest);
		token.setFromMobile(isFromMobile);
		token.setIp(ip);

		AuthenticationException exception = null;
		// String captcha = (String)
		// session.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
		logger.info("为了验证登录用户而封装的token为" + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));
		Subject currentUser = getSubject(request, response);
		try {
			logger.info("对用户[" + username + "]进行登录验证..验证开始");
			currentUser.login(token);
			logger.info("对用户[" + username + "]进行登录验证..验证通过");

		} catch (UnknownAccountException uae) {
			logger.info("对用户[" + username + "]进行登录验证..验证未通过,未知账户");
			token.setError("未知账户");
			exception = uae;
		} catch (IncorrectCredentialsException ice) {
			logger.info("对用户[" + username + "]进行登录验证..验证未通过,错误的凭证");
			token.setError("密码不正确");
			exception = ice;
		} catch (LockedAccountException lae) {
			logger.info("对用户[" + username + "]进行登录验证..验证未通过,账户已锁定");
			token.setError("账户已锁定");
			exception = lae;
		} catch (ExcessiveAttemptsException eae) {
			logger.info("对用户[" + username + "]进行登录验证..验证未通过,错误次数过多");
			token.setError("用户名或密码错误次数过多");
			exception = eae;
		} catch (AuthenticationException ae) {
			logger.info("对用户[" + username + "]进行登录验证..验证未通过,堆栈轨迹如下");
			ae.printStackTrace();
			token.setError("用户名或密码不正确");
			exception = ae;
		}
		// 验证是否登录成功
		if (currentUser.isAuthenticated()) {
			logger.info("用户[" + username + "]登录认证通过(这里可以进行一些认证通过后的一些系统参数初始化操作)");
			return onLoginSuccess(token, currentUser, request, response);
		} else {
			token.clear();
			return onLoginFailure(token, exception, request, response);
		}

	}

	@Override
	protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
		if (isAjaxRequest(request)) {
			try {
				HttpServletRequest httpservletrequest = ((HttpServletRequest) request);
				String user = (String) httpservletrequest.getSession().getAttribute(User.SESSION_USER_JSON);
				this.writeJson(response, user);
				return false;
			} catch (IOException e1) {
				e1.printStackTrace();
				return false;
			}

		} else {
			return super.onLoginSuccess(token, subject, request, response);
		}

	}

	private void writeJson(ServletResponse response, String message) throws IOException {
		PrintWriter out = null;
		try {
			HttpServletResponse httpServletResponse = (HttpServletResponse) response;
			httpServletResponse.setCharacterEncoding("UTF-8");
			response.setContentType("text/plain");
			out = httpServletResponse.getWriter();
			out.write(message);
			out.flush();
		} catch (IOException e) {
			e.printStackTrace();
			throw e;
		} finally {
			if (out != null) {
				out.close();
			}
		}
	}

	@Override
	protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
		if (isAjaxRequest(request)) {
			try {
				UserToken usertoken = (UserToken) token;
				String message = "{\"error\":\"" + usertoken.getError() + "\"}";
				this.writeJson(response, message);
				return true;
			} catch (IOException e1) {
				e1.printStackTrace();
				return false;
			}
		} else {

			return super.onLoginFailure(token, e, request, response);
		}

	}

	private boolean isAjaxRequest(ServletRequest request) {
		String head = ((HttpServletRequest) request).getHeader("X-Requested-With");
		return "XMLHttpRequest".equalsIgnoreCase(head);

	}

	/**
	 * 所有请求都会经过的方法。
	 */
	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		System.out.println("==================");
		boolean isajax = this.isAjaxRequest(request);
		if (isLoginRequest(request, response)) {
			if (isLoginSubmission(request, response)) {
				return executeLogin(request, response);
			} else {
				return true;
			}
		} else {
			if (isajax) {
				this.writeJson(response, User.SESSIONTIMEOUT);
			} else {
				// saveRequestAndRedirectToLogin(request, response);
				return super.onAccessDenied(request, response);
			}
			return false;
		}
	}

	@Override
	protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) {
		request.setAttribute(getFailureKeyAttribute(), ae);
	}

}
